package city.spring.configure.resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 定义OAuth2请求匹配器。
 *
 * @author HouKunLin
 * @date 2019/9/26 0026 17:14
 */
public class OAuth2RequestedMatcher implements RequestMatcher {
    private final static Logger logger = LoggerFactory.getLogger(OAuth2RequestedMatcher.class);
    private final boolean isDebug = logger.isDebugEnabled();
    private final OrRequestMatcher orRequestMatcher;
    private final String bearer = OAuth2AccessToken.BEARER_TYPE.toLowerCase();

    public OAuth2RequestedMatcher() {
        this(Collections.emptyList(), Collections.emptyList());
    }

    public OAuth2RequestedMatcher(String... allowedAntPatterns) {
        this(Collections.emptyList(), Arrays.asList(allowedAntPatterns));
    }

    public OAuth2RequestedMatcher(RequestMatcher... allowedRequestMatcher) {
        this(Arrays.asList(allowedRequestMatcher), Collections.emptyList());
    }

    public OAuth2RequestedMatcher(List<RequestMatcher> allowedRequestMatcher, List<String> allowedAntPatterns) {
        List<RequestMatcher> requestMatchers = new ArrayList<>(allowedRequestMatcher);
        for (String allowedAntPattern : allowedAntPatterns) {
            requestMatchers.add(new AntPathRequestMatcher(allowedAntPattern));
        }
        this.orRequestMatcher = new OrRequestMatcher(requestMatchers);
    }

    /**
     * 决定哪些请求经过资源服务认证处理（结果为true时），当结果为false时不经过资源程序处理，直接进入到下一步安全程序处理流程
     *
     * @param request 当前请求
     * @return true：经过资源服务程序处理，false：经过下一级的安全程序处理
     */
    @Override
    public boolean matches(HttpServletRequest request) {
        // 判断某些路径是否可以直接跳过资源服务程序
        if (orRequestMatcher.matches(request)) {
            // true：跳过资源服务程序，false：需要进一步确认
            // 不需要经过资源服务程序进行处理，也就是这个URI不需要进行权限验证操作
            return false;
        }

        boolean result = hasAccessToken(request);
        if (isDebug) {
            logger.debug("have access token:{} {} {} {}",
                    result,
                    request.getMethod(),
                    request.getRequestURI(),
                    request.getClass().getName());
        }
        return result;
    }

    public boolean hasAccessToken(HttpServletRequest request) {
        String value = request.getHeader(HttpHeaders.AUTHORIZATION);
        // 判断来源请求是否包含oauth2授权信息,这里授权信息来源可能是头部的Authorization值以Bearer开头,或者是请求参数中包含access_token参数,满足其中一个则匹配成功
        if (value != null && value.toLowerCase().startsWith(bearer)) {
            return true;
        }
        return request.getParameter(OAuth2AccessToken.ACCESS_TOKEN) != null;
    }
}